<template>
  <div class="device-base-info">
    <header class="page-header">
      <a-button type="primary" :loading="loading" @click="fetchMetrics">
        <Icon icon="ant-design:reload-outlined" />
        刷新
      </a-button>
    </header>
    <section class="card summary-grid">
      <div v-for="card in summaryCards" :key="card.key" class="summary-card" :class="`is-${card.accent}`">
        <div class="summary-card__content">
          <div class="summary-card__text">
            <p class="summary-card__title">{{ card.title }}</p>
            <p class="summary-card__value">{{ card.value }}</p>
            <p class="summary-card__sub" v-if="card.subText">
              <span>{{ card.subText }}</span>
              <a v-if="card.action" class="summary-card__action" @click="card.action.handler">{{
                card.action.label
              }}</a>
            </p>
          </div>
          <div class="summary-card__icon">
            <Icon :icon="card.icon" :size="32" />
          </div>
        </div>
      </div>
    </section>

    <section class="card base-fields">
      <header class="section-title">
        <Icon icon="ant-design:info-circle-outlined" :size="16" />
        <span>{{ t('tb.baseInfo.sections.basic') }}</span>
      </header>
      <div class="field-grid">
        <div v-for="field in basicFields" :key="field.key" class="field-item">
          <p class="field-item__label">{{ field.label }}</p>
          <p class="field-item__value">{{ field.value || '--' }}</p>
        </div>
      </div>
    </section>

    <NetworkTrendChart
      :data="normalizedMetrics.network.history || []"
      :vpn="normalizedMetrics.network.vpn"
      @refresh="emit('refresh-network')"
    />

    <ResourceUsageCharts v-if="normalizedMetrics.resources" :data="normalizedMetrics.resources" />

    <section class="pipelines">
      <div v-for="card in pipelineCards" :key="card.key" class="card pipeline-card">
        <div class="pipeline-card__header">
          <div class="pipeline-card__title">
            <Icon icon="ant-design:bar-chart-outlined" :size="16" />
            <span>{{ card.label }}</span>
          </div>
          <a-button type="link" size="small" @click="emit('create-pipeline-task', card.key)">
            + {{ t('tb.baseInfo.pipeline.actions.create', { label: card.shortLabel }) }}
          </a-button>
        </div>
        <div class="pipeline-card__stats">
          <div v-for="stat in card.statsList" :key="stat.key" class="stat-item">
            <p class="stat-item__label">{{ stat.label }}</p>
            <div class="stat-item__value">
              <span>{{ stat.value ?? '--' }}</span>
              <Icon v-if="stat.trendIcon" :icon="stat.trendIcon" :style="{ color: stat.trendColor }" />
            </div>
          </div>
        </div>
        <div class="pipeline-card__body">
          <div class="job-block">
            <div class="job-block__header">
              <span>{{ t('tb.baseInfo.pipeline.blocks.current', { label: card.shortLabel }) }}</span>
              <Tag v-if="card.currentJob?.status" :color="jobStatusColor(card.currentJob?.status)">
                {{ jobStatusText(card.currentJob?.status) }}
              </Tag>
            </div>
            <template v-if="card.currentJob">
              <p class="job-block__title">{{ card.currentJob.name }}</p>
              <p class="job-block__meta">
                {{ card.currentJob.typeLabel }}
                <span v-if="card.currentJob.rangeText"> · {{ card.currentJob.rangeText }}</span>
              </p>
              <div class="progress-bar slim">
                <div class="progress-bar__fill" :style="{ width: (card.currentJob.progress || 0) + '%' }"></div>
              </div>
            </template>
            <template v-else>
              <p class="job-block__placeholder">
                {{ t('tb.baseInfo.pipeline.placeholders.noCurrent') }}
              </p>
            </template>
          </div>
          <div class="job-block">
            <div class="job-block__header">
              <span>{{ t('tb.baseInfo.pipeline.blocks.history', { label: card.shortLabel }) }}</span>
              <a-button
                type="link"
                size="small"
                :disabled="!card.lastJob"
                @click="card.lastJob ? emit('view-job-detail', card.lastJob) : undefined"
              >
                {{ t('tb.baseInfo.pipeline.actions.viewDetail') }}
              </a-button>
            </div>
            <template v-if="card.lastJob">
              <p class="job-block__title">{{ card.lastJob.name }}</p>
              <p class="job-block__meta">{{ card.lastJob.rangeText }}</p>
            </template>
            <template v-else>
              <p class="job-block__placeholder">{{ t('tb.baseInfo.pipeline.placeholders.noHistory') }}</p>
            </template>
          </div>
        </div>
      </div>
    </section>

    <section class="card alerts-card">
      <header class="section-title section-title--space">
        <div class="section-title__left">
          <Icon icon="ant-design:alert-outlined" :size="16" />
          <span>{{ t('tb.baseInfo.alerts.title') }}</span>
        </div>
        <a class="link" @click="emit('view-alert-center')">{{ t('tb.baseInfo.alerts.more') }}</a>
      </header>
      <div v-if="alertCards.length" class="alerts-grid">
        <div v-for="alert in alertCards" :key="alert.id" class="alert-card" :class="`is-${alert.severity}`">
          <div class="alert-card__header">
            <p class="alert-card__title">{{ alert.title }}</p>
            <span class="alert-card__tag">{{ alert.levelLabel }}</span>
          </div>
          <p class="alert-card__desc">{{ alert.description }}</p>
          <p class="alert-card__time">{{ alert.happenAt }}</p>
          <div class="alert-card__actions">
            <a-button size="small" type="link" @click="emit('handle-alert', alert)">
              {{ t('tb.baseInfo.alerts.handle') }}
            </a-button>
            <a-button size="small" type="link" @click="emit('ignore-alert', alert)">
              {{ t('tb.baseInfo.alerts.ignore') }}
            </a-button>
          </div>
        </div>
      </div>
      <div v-else class="empty-alert">{{ t('tb.baseInfo.alerts.empty') }}</div>
    </section>
  </div>
</template>

<script setup lang="ts">
  import { computed, ref, watch } from 'vue';
  import dayjs from 'dayjs';
  import { Icon } from '/@/components/Icon';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { Tag } from 'ant-design-vue';
  import type { DeviceInfo } from '/@/api/tb/device';
  import type { DeviceBaseInfoMetrics, PipelineMetric, PipelineKey } from './types';
  import type { DeviceMetricsApiResponse } from './types';
  import NetworkTrendChart from './components/NetworkTrendChart.vue';
  import ResourceUsageCharts from './components/ResourceUsageCharts.vue';
  import { getDeviceMetrics } from '/@/api/tb/device';

  const props = defineProps<{
    deviceInfo?: DeviceInfo | null;
    metrics?: DeviceBaseInfoMetrics | null;
  }>();

  const emit = defineEmits([
    'refresh-network',
    'create-pipeline-task',
    'view-job-detail',
    'view-alert-center',
    'handle-alert',
    'ignore-alert',
  ]);

  const { t } = useI18n();

  const metricsData = ref<DeviceBaseInfoMetrics | null>(null);
  const loading = ref(false);

  const normalizedMetrics = computed<DeviceBaseInfoMetrics>(() => {
    if (props.metrics) return props.metrics;
    if (metricsData.value) return metricsData.value;
    return createMockMetrics(props.deviceInfo);
  });

  function safeNumber(value: unknown, fallback = 0) {
    const num = Number(value);
    return Number.isFinite(num) ? num : fallback;
  }

  function formatSize(bytes?: number) {
    const normalized = safeNumber(bytes);
    if (normalized <= 0) {
      return { value: 0, unit: 'B' };
    }
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(normalized) / Math.log(k));
    return {
      value: parseFloat((normalized / Math.pow(k, i)).toFixed(2)),
      unit: sizes[i],
    };
  }

  async function fetchMetrics() {
    if (!props.deviceInfo?.id?.id) return;
    loading.value = true;
    try {
      const res = (await getDeviceMetrics(props.deviceInfo.id.id)) as DeviceMetricsApiResponse;

      // Transform API response to UI model
      const mock = createMockMetrics(props.deviceInfo); // Use mock as base for missing fields

      const memoryTotalBytes = safeNumber(res?.memoryTotalBytes);
      const memoryAvailableBytes = safeNumber(res?.memoryAvailableBytes);
      const diskTotalBytes = safeNumber(res?.diskTotalBytes);
      const diskAvailableBytes = safeNumber(res?.diskAvailableBytes);

      const memTotal = formatSize(memoryTotalBytes);
      const memUsed = formatSize(Math.max(memoryTotalBytes - memoryAvailableBytes, 0));
      const diskTotal = formatSize(diskTotalBytes);
      const diskUsed = formatSize(Math.max(diskTotalBytes - diskAvailableBytes, 0));

      const resources = {
        cpu: {
          used: 0,
          total: 100,
          unit: '%',
          percent: Math.round(safeNumber(res?.cpuLoadPercentage)),
        },
        memory: {
          used: memUsed.value,
          total: memTotal.value,
          unit: memTotal.unit,
          percent: Math.round(safeNumber(res?.memoryUsagePercentage)),
        },
        disk: {
          used: diskUsed.value,
          total: diskTotal.value,
          unit: diskTotal.unit,
          percent: Math.round(safeNumber(res?.diskUsagePercentage)),
        },
      };

      // Network - 处理时间序列数据
      const uploadData = Array.isArray(res?.networkUploadBytes) ? res.networkUploadBytes : [];
      const downloadData = Array.isArray(res?.networkDownloadBytes) ? res.networkDownloadBytes : [];

      // 转换为图表所需的格式
      const networkHistory = [];
      const maxLength = Math.max(uploadData.length, downloadData.length);
      
      for (let i = 0; i < maxLength; i++) {
        const uploadPoint = uploadData[i];
        const downloadPoint = downloadData[i];
        
        networkHistory.push({
          timestamp: uploadPoint?.date || downloadPoint?.date || '',
          upload: safeNumber(uploadPoint?.value, 0),
          download: safeNumber(downloadPoint?.value, 0),
        });
      }

      // 获取最新值用于显示当前网速
      const latestUpload = uploadData.length > 0 ? uploadData[uploadData.length - 1]?.value : 0;
      const latestDownload = downloadData.length > 0 ? downloadData[downloadData.length - 1]?.value : 0;
      
      const uploadSpeed = formatSize(latestUpload);
      const downloadSpeed = formatSize(latestDownload);

      const network = {
        ...mock.network,
        upload: {
          title: t('tb.baseInfo.network.upload'),
          speed: uploadSpeed.value,
          unit: `${uploadSpeed.unit}/s`,
          percent: 0,
          trend: 'flat' as const,
        },
        download: {
          title: t('tb.baseInfo.network.download'),
          speed: downloadSpeed.value,
          unit: `${downloadSpeed.unit}/s`,
          percent: 0,
          trend: 'flat' as const,
        },
        vpn: {
          status: res?.vpnStatus === 1 ? ('connected' as const) : ('disconnected' as const),
          label: res?.vpnStatus === 1 ? t('tb.baseInfo.network.connected') : t('tb.baseInfo.network.disconnected'),
          duration: `${Math.max(safeNumber(res?.vpnTime), 0)}s`,
        },
        history: networkHistory,
      };

      const rawAlertList = Array.isArray(res?.alertList) ? res?.alertList : Object.values(res?.alertList || {});
      const alerts =
        rawAlertList?.filter(Boolean).map((item: any, index: number) => ({
          id: item?.id || `alert-${index}`,
          title: item?.type || item?.title || t('tb.baseInfo.alerts.title'),
          description: item?.details || item?.description || '',
          happenAt: item?.createdTime ? dayjs(item.createdTime).format('YYYY-MM-DD HH:mm:ss') : '',
          severity: item?.severity === 'CRITICAL' ? 'critical' : 'warning',
          levelLabel: item?.severity || '',
        })) || [];

      const alertCount = safeNumber(res?.alertCount, alerts.length);

      metricsData.value = {
        ...mock,
        summary: {
          ...mock.summary,
          alerts: {
            value: alertCount,
            actionText: t('tb.baseInfo.summary.needHandle'),
          },
        },
        resources,
        network,
        alerts,
      };
    } catch (error) {
      console.error('Failed to fetch device metrics:', error);
    } finally {
      loading.value = false;
    }
  }

  watch(
    () => props.deviceInfo?.id?.id,
    (newId) => {
      if (newId) {
        fetchMetrics();
      }
    },
    { immediate: true },
  );

  const summaryCards = computed(() => {
    const summary = normalizedMetrics.value.summary;
    return [
      {
        key: 'online',
        title: t('tb.baseInfo.summary.onlineStatus'),
        value: summary.onlineStatus.label || '--',
        subText: summary.onlineStatus.lastSyncAt
          ? `${t('tb.baseInfo.summary.lastSync')}：${summary.onlineStatus.lastSyncAt}`
          : '',
        icon: 'ant-design:safety-certificate-filled',
        accent: summary.onlineStatus.status === 'error' ? 'danger' : 'success',
      },
      {
        key: 'alerts',
        title: t('tb.baseInfo.summary.alertCount'),
        value: summary.alerts.value ?? 0,
        subText: summary.alerts.actionText || t('tb.baseInfo.summary.needHandle'),
        icon: 'ant-design:warning-filled',
        accent: 'warning',
        action: {
          label: t('tb.baseInfo.alerts.more'),
          handler: () => emit('view-alert-center'),
        },
      },
      {
        key: 'today',
        title: t('tb.baseInfo.summary.todayDataVolume'),
        value: summary.todayDataVolume.value || '--',
        subText: formatTrend(summary.todayDataVolume.changePercent),
        icon: 'ant-design:pie-chart-filled',
        accent: 'info',
      },
      {
        key: 'datasource',
        title: t('tb.baseInfo.summary.datasourceTotal'),
        value: summary.totalDatasource.value ?? '--',
        subText: formatTrend(summary.totalDatasource.changePercent),
        icon: 'ant-design:database-filled',
        accent: 'info',
      },
    ];
  });

  const basicFields = computed(() => {
    const info = props.deviceInfo;
    const overrides = normalizedMetrics.value.basicInfo || {};
    return [
      { key: 'nodeName', label: t('tb.baseInfo.fields.nodeName'), value: overrides.nodeName ?? info?.name },
      {
        key: 'gatewayId',
        label: t('tb.baseInfo.fields.gatewayId'),
        value: overrides.gatewayId ?? info?.externalId?.id ?? info?.id?.id,
      },
      { key: 'ip', label: t('tb.baseInfo.fields.ip'), value: overrides.ip },
      { key: 'group', label: t('tb.baseInfo.fields.group'), value: overrides.group },
      { key: 'operator', label: t('tb.baseInfo.fields.operator'), value: overrides.operator },
      { key: 'brand', label: t('tb.baseInfo.fields.brand'), value: overrides.brand },
    ];
  });

  const pipelineCards = computed(() => {
    const pipelines = normalizedMetrics.value.pipelines;
    return [
      buildPipelineCard('collect', t('tb.baseInfo.pipeline.collect'), pipelines.collect),
      buildPipelineCard('deliver', t('tb.baseInfo.pipeline.deliver'), pipelines.deliver),
      buildPipelineCard('receive', t('tb.baseInfo.pipeline.receive'), pipelines.receive),
    ];
  });

  const alertCards = computed(() => normalizedMetrics.value.alerts ?? []);

  function buildPipelineCard(key: PipelineKey, label: string, metric?: PipelineMetric) {
    const stats = metric?.stats;
    const statsList = [
      {
        key: 'taskCount',
        label: t('tb.baseInfo.pipeline.stats.taskCount'),
        value: stats?.taskCount,
        trend: stats?.taskTrend,
      },
      {
        key: 'orderCount',
        label: t('tb.baseInfo.pipeline.stats.orderCount'),
        value: stats?.orderCount,
        trend: stats?.orderTrend,
      },
      {
        key: 'todayFinished',
        label: t('tb.baseInfo.pipeline.stats.todayFinished'),
        value: stats?.todayFinishedSize,
        trend: stats?.todayTrend,
      },
      {
        key: 'monthFinished',
        label: t('tb.baseInfo.pipeline.stats.monthFinished'),
        value: stats?.thisMonthFinishedSize,
        trend: stats?.monthTrend,
      },
      {
        key: 'inProgress',
        label: t('tb.baseInfo.pipeline.stats.inProgress'),
        value: stats?.inProgressCount,
        trend: stats?.runningTrend,
      },
    ].map((item) => ({
      ...item,
      trendIcon:
        item.trend === 'down'
          ? 'ant-design:arrow-down-outlined'
          : item.trend === 'up'
            ? 'ant-design:arrow-up-outlined'
            : null,
      trendColor: item.trend === 'down' ? '#f97316' : item.trend === 'up' ? '#22c55e' : '#94a3b8',
    }));

    return {
      key,
      label,
      shortLabel: label.replace(/状态$/, ''),
      statsList,
      currentJob: metric?.currentJob,
      lastJob: metric?.lastJob,
    };
  }

  function jobStatusColor(status?: string) {
    switch (status) {
      case 'processing':
        return 'success';
      case 'paused':
        return 'warning';
      case 'success':
        return 'blue';
      case 'stopped':
        return 'error';
      default:
        return 'default';
    }
  }

  function jobStatusText(status?: string) {
    switch (status) {
      case 'processing':
        return t('tb.baseInfo.pipeline.statuses.processing');
      case 'paused':
        return t('tb.baseInfo.pipeline.statuses.paused');
      case 'success':
        return t('tb.baseInfo.pipeline.statuses.success');
      case 'stopped':
        return t('tb.baseInfo.pipeline.statuses.stopped');
      default:
        return '';
    }
  }

  function formatTrend(value?: number) {
    if (value === null || value === undefined) return '';
    const prefix = value >= 0 ? '+' : '';
    return `${t('tb.baseInfo.summary.compareYesterday')}：${prefix}${value}%`;
  }

  function createMockMetrics(device?: DeviceInfo | null): DeviceBaseInfoMetrics {
    const now = dayjs().format('YYYY-MM-DD HH:mm:ss');
    return {
      summary: {
        onlineStatus: {
          label: device?.active ? '正常' : '离线',
          status: device?.active ? 'normal' : 'error',
          lastSyncAt: now,
        },
        alerts: {
          value: 0,
          actionText: t('tb.baseInfo.summary.needHandle'),
        },
        todayDataVolume: {
          value: '62.3GB',
          changePercent: 12.5,
        },
        totalDatasource: {
          value: 34,
          changePercent: 12.5,
        },
      },
      basicInfo: {
        nodeName: device?.name || 'Node01',
        gatewayId: device?.id?.id || 'AXEFAHJAUSD',
        ip: '156.245.12.26',
        group: '东区-501',
        operator: '四川第一医院',
        brand: '海贝',
      },
      network: {
        upload: { title: t('tb.baseInfo.network.upload'), speed: 2.5, unit: 'MB/s', percent: 35, trend: 'up' },
        download: { title: t('tb.baseInfo.network.download'), speed: 8.3, unit: 'MB/s', percent: 70, trend: 'down' },
        vpn: { status: 'connected', label: t('tb.baseInfo.network.connected'), duration: '12天8小时' },
        history: Array.from({ length: 20 }).map((_, i) => ({
          timestamp: dayjs()
            .subtract(20 - i, 'minute')
            .format('HH:mm'),
          upload: Number((Math.random() * 5).toFixed(1)),
          download: Number((Math.random() * 10).toFixed(1)),
        })),
      },
      resources: {
        cpu: { used: 0, total: 100, unit: '%', percent: 60 },
        memory: { used: 12.5, total: 16, unit: 'GB', percent: 78 },
        disk: { used: 136, total: 512, unit: 'GB', percent: 26 },
      },
      pipelines: {
        collect: {
          key: 'collect',
          label: t('tb.baseInfo.pipeline.collect'),
          stats: {
            taskCount: 12,
            orderCount: 8,
            todayFinishedSize: 28.5,
            thisMonthFinishedSize: 28.5,
            inProgressCount: 2,
            taskTrend: 'up',
            orderTrend: 'up',
            todayTrend: 'up',
            monthTrend: 'flat',
            runningTrend: 'up',
          },
          currentJob: {
            id: 'collect-current',
            name: '[药品数据接取]【mysql】采集',
            typeLabel: t('tb.baseInfo.pipeline.statuses.processing'),
            progress: 68,
            status: 'processing',
            rangeText: '进行中 · 开始时间: 2025-08-31 12:00:00',
          },
          lastJob: {
            id: 'collect-last',
            name: '[药品数据接取]【mysql】采集',
            rangeText: '2025-09-01 00:00:00 至 2025-09-02 00:00:00',
          },
        },
        deliver: {
          key: 'deliver',
          label: t('tb.baseInfo.pipeline.deliver'),
          stats: {
            taskCount: 12,
            orderCount: 5,
            todayFinishedSize: 28.5,
            thisMonthFinishedSize: 28.5,
            inProgressCount: 2,
            taskTrend: 'up',
            orderTrend: 'down',
            todayTrend: 'up',
            monthTrend: 'up',
            runningTrend: 'down',
          },
          currentJob: {
            id: 'deliver-current',
            name: '[药品数据交付]【mysql】交付',
            typeLabel: t('tb.baseInfo.pipeline.statuses.processing'),
            progress: 68,
            status: 'paused',
            rangeText: '进行中 · 开始时间: 2025-08-31 12:00:00',
          },
          lastJob: {
            id: 'deliver-last',
            name: '[药品数据交付]【mysql】交付',
            rangeText: '2025-09-01 00:00:00 至 2025-09-02 00:00:00',
          },
        },
        receive: {
          key: 'receive',
          label: t('tb.baseInfo.pipeline.receive'),
          stats: {
            taskCount: 12,
            orderCount: 5,
            todayFinishedSize: 28.5,
            thisMonthFinishedSize: 28.5,
            inProgressCount: 2,
            taskTrend: 'up',
            orderTrend: 'up',
            todayTrend: 'flat',
            monthTrend: 'flat',
            runningTrend: 'up',
          },
          currentJob: {
            id: 'receive-current',
            name: '[药品数据交付]【mysql】接收',
            typeLabel: t('tb.baseInfo.pipeline.statuses.processing'),
            progress: 68,
            status: 'processing',
            rangeText: '进行中 · 开始时间: 2025-08-31 12:00:00',
          },
          lastJob: {
            id: 'receive-last',
            name: '[药品数据交付]【mysql】接收',
            rangeText: '2025-09-01 00:00:00 至 2025-09-02 00:00:00',
          },
        },
      },
      alerts: [
        {
          id: 'alert-1',
          title: 'CPU超高告警',
          description: 'CPU使用率持续10分钟超过90%，可能影响数据采集性能',
          happenAt: now,
          severity: 'critical',
          levelLabel: '严重',
        },
        {
          id: 'alert-2',
          title: '内存使用率偏高',
          description: '内存使用率85%，建议优化数据采集任务',
          happenAt: now,
          severity: 'warning',
          levelLabel: '警告',
        },
      ],
    };
  }
</script>

<style scoped lang="less">
  .device-base-info {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  .page-header {
    display: flex;
    justify-content: flex-start;
    align-items: center;
    margin-bottom: 0;
  }

  .card {
    background: #fff;
    border-radius: 16px;
    padding: 16px;
    box-shadow: 0 6px 20px rgba(15, 23, 42, 0.08);
  }

  .summary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 12px;
  }

  .summary-card {
    border-radius: 14px;
    padding: 16px 20px;
    background: #f8fafc;

    &__content {
      display: flex;
      align-items: flex-start;
      justify-content: space-between;
      gap: 12px;
    }

    &__title {
      font-size: 13px;
      color: #475569;
      margin-bottom: 4px;
    }

    &__value {
      font-size: 24px;
      font-weight: 600;
      color: #0f172a;
    }

    &__sub {
      font-size: 12px;
      color: #64748b;
      margin-top: 4px;
    }

    &__action {
      margin-left: 8px;
      color: #2563eb;
    }

    &__icon {
      width: 48px;
      height: 48px;
      border-radius: 12px;
      display: flex;
      align-items: center;
      justify-content: center;
      background: rgba(79, 123, 254, 0.1);
      color: #4f7bfe;
    }

    &.is-success &__icon {
      background: rgba(34, 197, 94, 0.15);
      color: #22c55e;
    }

    &.is-warning &__icon {
      background: rgba(245, 158, 11, 0.15);
      color: #f59e0b;
    }

    &.is-danger &__icon {
      background: rgba(239, 68, 68, 0.15);
      color: #ef4444;
    }
    &.is-info &__icon {
      background: rgba(59, 130, 246, 0.12);
      color: #3b82f6;
    }
  }

  .section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 600;
    margin-bottom: 16px;
    color: #0f172a;

    &--space {
      justify-content: space-between;
    }
  }

  .field-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    row-gap: 12px;
    column-gap: 16px;
  }

  .field-item__label {
    font-size: 12px;
    color: #94a3b8;
    margin-bottom: 4px;
  }

  .field-item__value {
    font-size: 16px;
    font-weight: 600;
    color: #0f172a;
  }

  .progress-bar {
    width: 100%;
    height: 6px;
    border-radius: 999px;
    background: #e2e8f0;
    overflow: hidden;

    &.slim {
      height: 4px;
    }

    &__fill {
      height: 100%;
      border-radius: inherit;
      background: linear-gradient(90deg, #4f7bfe, #6f9bff);
      transition: width 0.3s ease;
    }
  }

  .pipelines {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 16px;
  }

  .pipeline-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
  }

  .pipeline-card__title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 600;
  }

  .pipeline-card__stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 16px;
  }

  .stat-item {
    background: #f8fafc;
    border-radius: 12px;
    padding: 10px 12px;

    &__label {
      font-size: 12px;
      color: #94a3b8;
      margin-bottom: 4px;
    }

    &__value {
      display: flex;
      align-items: center;
      gap: 6px;
      font-size: 16px;
      font-weight: 600;
      color: #0f172a;
    }
  }

  .pipeline-card__body {
    display: grid;
    gap: 12px;
  }

  .job-block {
    background: #f8fafc;
    border-radius: 14px;
    padding: 12px 16px;

    &__header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      font-size: 13px;
      margin-bottom: 8px;
      color: #475569;
    }

    &__title {
      font-weight: 600;
      margin-bottom: 4px;
    }

    &__meta {
      font-size: 12px;
      color: #94a3b8;
      margin-bottom: 10px;
    }

    &__placeholder {
      color: #94a3b8;
    }
  }

  .alerts-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
  }

  .alert-card {
    border-radius: 12px;
    padding: 16px;
    border: 1px solid transparent;

    &__header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 8px;
    }

    &__title {
      font-weight: 600;
    }

    &__tag {
      font-size: 12px;
      padding: 2px 8px;
      border-radius: 999px;
      background: rgba(0, 0, 0, 0.08);
    }

    &__desc {
      font-size: 13px;
      color: #475569;
      margin-bottom: 8px;
    }

    &__time {
      font-size: 12px;
      color: #94a3b8;
      margin-bottom: 12px;
    }

    &__actions {
      display: flex;
      gap: 8px;
    }

    &.is-critical {
      background: rgba(239, 68, 68, 0.08);
      border-color: rgba(239, 68, 68, 0.25);
    }

    &.is-warning {
      background: rgba(251, 191, 36, 0.12);
      border-color: rgba(251, 191, 36, 0.3);
    }
  }

  .empty-alert {
    text-align: center;
    color: #94a3b8;
    padding: 24px 0;
  }

  .link {
    color: #2563eb;
    cursor: pointer;
  }
</style>
